/* eslint-disable @typescript-eslint/no-var-requires */
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const ejs = require('ejs');
const { exec } = require('child_process');
const { find } = require('lodash');
const { pinyin } = require('pinyin-pro');
const root = process.cwd();
const apisPath = '/src/application/apis';
const typePath = '/src/types/apiType.ts';

// type映射
const convertTypeMap = {
  number: 'number',
  integer: 'number',
  string: 'string',
  object: 'object',
  boolean: 'boolean'
};

let globalTags = [];
const formatParams = (params) => {
  let paramsStr = '';
  params.forEach((item, index) => {
    if (index === 0) {
      paramsStr = '{ ';
    }
    paramsStr += `${item.name}${item.required ? '' : '?'}: ${item.type}; `;
    if (index === params.length - 1) {
      paramsStr += '}';
    }
  });
  return paramsStr;
};
const capitalizedWord = (word) => word.charAt(0).toUpperCase() + word.slice(1);

const lowerWord = (word) => word.charAt(0).toLowerCase() + word.slice(1);
const handleRefType = (str) => {
  const _str = str
    .split('/')
    .pop()
    .replace(/«/g, '')
    .replace(/»/g, '')
    .replace('接口返回对象', 'Response');
  const formatStr = _str.replace(/[\u4e00-\u9fa5]/g, '');
  if (!formatStr) {
    const a = pinyin(_str, { toneType: 'none', type: 'array' });
    return a.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');
  }
  return formatStr;
};

const getType = (typeObj) => {
  if (typeObj.type) {
    const typeTmp = convertTypeMap[typeObj.type];
    if (typeTmp === undefined && typeObj.type === 'array') {
      return getArrayType(typeObj);
    } else {
      return typeTmp;
    }
  } else if (typeObj.$ref) {
    return handleRefType(typeObj.$ref);
  }
  return undefined;
};

const getArrayType = (detail) => {
  const typeRes = getType(detail.items);
  if (typeRes) {
    return `${typeRes}[]`;
  } else {
    return undefined;
  }
};
// /alert/detail/{id}   /alert/list
const getServiceName = (path, fetchMethod) => {
  const plist = path.split('/').filter((item) => item);
  plist.push('by');
  plist.push(fetchMethod);

  return plist
    .map((item, index) => {
      if (item.indexOf('{') >= 0) {
        // 去掉{}
        item = item.slice(1, item.length - 1);
      }
      if (index === 0) {
        return item;
      } else {
        return capitalizedWord(item);
      }
    })
    .join('');
};

const responseType = new Set();

const formatFile = (filePath) => {
  exec(`npx eslint ${filePath}  --fix & prettier --write ${filePath}`, (error, stdout, stderr) => {
    if (error) {
      console.error(`执行 Prettier 命令时出现错误：${error.message}`);
      return;
    }
    if (stderr) {
      console.error(`Prettier 命令返回错误：${stderr}`);
      return;
    }
    console.log(`${filePath}文件已成功格式化！`);
  });
};

// 处理apiController
const handleService = (paths) => {
  let apiMap = new Map();
  globalTags.forEach((item) => {
    apiMap.set(item, {});
  });

  for (let prop in paths) {
    const content = paths[prop];
    const methodsArr = Object.keys(content);
    methodsArr.forEach((name) => {
      const orgVal = content[name];
      const curTag = orgVal.tags.pop();
      const tagInfo = globalTags.find((item) => item.name === curTag);
      const { description, summary, parameters } = orgVal;

      // 引用参数 常用于入参是body的情况
      const importType = tagInfo.importType;
      // 处理入参
      const queryParams = [];
      const pathParams = [];
      let bodyStr = '';
      for (let paramsItem of parameters) {
        // body query path
        const inValue = paramsItem.in;

        const convertItem = {
          name: paramsItem.name,
          type: getType(paramsItem),
          required: paramsItem.required
        };

        if (inValue === 'body') {
          // 只会有一个 body
          bodyStr = getType(paramsItem.schema);
          if (paramsItem.schema?.$ref) {
            if (!importType.includes(bodyStr)) {
              importType.push(bodyStr);
            }
          }
        } else if (inValue === 'query') {
          // such as /users?role=admin
          queryParams.push(convertItem);
        } else if (inValue === 'path') {
          // such as /users/{id}
          pathParams.push(convertItem);
        }
      }
      const requestType = bodyStr ? bodyStr : formatParams([...queryParams, ...pathParams]);

      // 处理出参
      // 返回值类型
      const normalResponse = orgVal.responses['200'] ? orgVal.responses['200'].schema : undefined;
      const resType = normalResponse ? getType(normalResponse) : undefined;
      // 后续type 过滤
      responseType.add(resType);
      const convertTmp = resType?.replace('[', '')?.replace(']', '');
      if (
        (normalResponse?.$ref || normalResponse?.items?.$ref) &&
        !importType.includes(convertTmp)
      ) {
        // 预设类型没有
        if (!Object.values(convertTypeMap).includes(convertTmp)) {
          importType.push(convertTmp);
        }
      }
      const data = {
        curTag,
        description,
        summary,
        method: name,
        path: prop,
        importType,
        requestType: requestType ? `params: ${requestType}` : '',
        responseType: resType
      };
      tagInfo.importType = importType;
      const value = { [getServiceName(prop, name)]: data };
      if (apiMap.has(curTag)) {
        const oArr = apiMap.get(curTag);
        let n = oArr.concat([value]);
        apiMap.set(curTag, n);
      } else {
        apiMap.set(curTag, [value]);
      }
    });
  }
  globalTags.forEach((tagFile) => {
    const fileName =
      lowerWord(tagFile.description.replace(/\s*/g, '')).replace(/Controller/, '') + '.ts';
    ejs.renderFile(
      root + '/scripts/templates/api.ejs',
      {
        importType: tagFile.importType,
        apis: apiMap.get(tagFile.name).sort()
      },
      (err, renderedText) => {
        if (err) {
          console.error('Error rendering EJS template:', err);
          return;
        }
        const filePath = root + `${apisPath}/${fileName}`;
        fs.writeFile(filePath, renderedText, (err) => {
          if (err) throw err;

          formatFile(filePath);
          console.log(`${fileName} is created successfully`);
        });
      }
    );
  });
};
/**
 * 得到 interface 定义字符串
 * @param prop interface 名称
 * @param definition interface 字段信息
 * @returns
 */
const getDefinitionType = (prop, definition) => {
  let cleanProp = prop.replace(/«/g, '').replace(/»/g, '').replace('接口返回对象', 'Response');

  const formatStr = cleanProp.replace(/[\u4e00-\u9fa5]/g, '');
  if (!formatStr) {
    const a = pinyin(cleanProp, { toneType: 'none', type: 'array' });
    cleanProp = a.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');
  } else {
    cleanProp = formatStr;
  }
  let properties = [];
  for (let p in definition.properties) {
    const detail = definition.properties[p];
    const cur = getType(detail);
    properties.push({
      name: p,
      type: cur,
      description: detail.description
    });
  }
  return {
    description: definition.description,
    name: cleanProp,
    properties
  };
};

// 处理type
const handleType = async (definitions) => {
  let dftStr = await ejs.renderFile(root + '/scripts/templates/type.ejs');
  dftStr += '\n\n';
  for (let prop in definitions) {
    const curValue = definitions[prop];
    if (curValue.type === 'object') {
      const data = getDefinitionType(prop, definitions[prop]);
      if (!responseType.has(data.name)) {
        dftStr += await ejs.renderFile(root + '/scripts/templates/typeItem.ejs', data);
      } else {
        const result = find(data.properties, { name: 'result' });
        dftStr += `${data.title ? '/**' + data.title + '*/\n' : ''} export type ${
          data.name
        } = ResponseResult<${result?.type}>\n`;
      }
    }
  }
  const path = root + `${typePath}`;
  fs.writeFile(path, dftStr, (err) => {
    if (err) throw err;
    formatFile(path);
    console.log(`type.ts is created successfully`);
  });
};

// main js

const mainFn = async () => {
  const res = await axios.get('http://121.40.147.219:8083/szbank/v2/api-docs', {
    headers: { Authorization: 'Basic c3piYW5rOlVuaWNvbUAxMjM=:' }
  });
  globalTags = res.data.tags.map((item) => ({
    ...item,
    importType: []
  }));
  handleService(res.data.paths);
  handleType(res.data.definitions);
};

mainFn();
